#include <openssl/crypto.h>
#include <openssl/x509.h>
#include <openssl/pem.h>
#include <openssl/ssl.h>
#include <openssl/err.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h> 
#include <unistd.h>
#include <cstdlib>
#include <iostream>

#include "SecuredConnector.h"

SecuredReadThread::SecuredReadThread(SSL* ssl_, int sfd, Connector* conn) : ReadThread(sfd, conn)
{
	ssl = ssl_;
}

void SecuredReadThread::run()
{
	std::string str;
	char buffer[256];
	int n;
	
	pthread_detach(pthread_self());
	
	str = "";
	while ((n = SSL_read(ssl,buffer,255)) > 0) //leer mensajes
	{
		str = str + buffer;
		
		if (buffer[n-1] == '\0') //ha llegado el final de la cadena
		{
			//ACCION QUE SE REALIZA AL RECIBIR UN MENSAJE
			connector->callBack(str);
			
			//reiniciar la cadena final a vacia para leer 
			//una nueva correctamente
			str = "";
		}
	}
	
	if (n == 0)
	{
		std::cerr << "ERROR conexion a servicio perdida" << std::endl;
		
		//cerrar el socket
		close(sockfd);
	}
}

SecuredConnector::SecuredConnector(const std::string& sh, int p) : Connector(sh, p)
{
	
}

SecuredConnector::~SecuredConnector()
{
	SSL_shutdown(ssl);
	SSL_free(ssl);
	SSL_CTX_free(ctx);
}

void SecuredConnector::openConnection()
{
	int n, k;
    struct sockaddr_in serv_addr;
    struct hostent *server;
	SSL_METHOD* meth;
	
	SSLeay_add_ssl_algorithms();
	meth = SSLv23_client_method();
	SSL_load_error_strings();
	ctx = SSL_CTX_new (meth);
	if (ctx == NULL)
	{
		std::cerr << "ERROR inicializacion de SSL" << std::endl;
		exit(1);
	}
	
    //char buffer[256];
	
    sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (sockfd < 0) 
	{
		std::cerr << "ERROR abriendo el socket" << std::endl;
		exit(1);
	}
	
    server = gethostbyname(serviceHost.c_str());
    if (server == NULL) 
	{
		std::cerr << "ERROR host no encontrado" << std::endl;
		exit(1);
    }
	
    bzero((char *) &serv_addr, sizeof(serv_addr));
	
    serv_addr.sin_family = AF_INET;
	
    bcopy((char *)server->h_addr, 
		  (char *)&serv_addr.sin_addr.s_addr,
		  server->h_length);
	
    serv_addr.sin_port = htons(port);
	
    if (connect(sockfd,(struct sockaddr*)&serv_addr,sizeof(serv_addr)) < 0)
	{
		std::cerr << "ERROR conexion al host fallida" << std::endl;
		exit(1);
	}
	
	//inicializacion de SSL
	ssl = SSL_new(ctx);
	if (ssl == NULL)
	{
		std::cerr << "ERROR creacion del contexto SSL" << std::endl;
	}
	
	SSL_set_fd(ssl, sockfd);
	if (SSL_connect(ssl) < 0)
	{
		std::cerr << "ERROR conexion mediante SSL" << std::endl;
	}
	
	readThread = new SecuredReadThread(ssl, sockfd, this);
	((SecuredReadThread*)readThread)->start();
}

void SecuredConnector::write(const std::string& msg)
{
	SSL_write(ssl, msg.c_str(), msg.length()+1);
}
